<html>
<head>
<link rel="shortcut icon" href="./favicon.ico">
<link rel="stylesheet" type="text/css" href="./style.css">
<link rel="canonical" href="./CDC_Word_Synchronizer.html">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="Synchronizes the transfer of a word of data from one clock domain to another, regardless of relative clock frequencies. Uses ready/valid handshakes at the sending and receiving ends, but these can be short-circuited for continuous transfers without backpressure: ignore `sending_ready` and tie `receiving_ready` high. Add `EXTRA_CDC_DEPTH` if you are running near the limits of your silicon (consult your vendor datasheets regarding metastability).">
<title>CDC Word Synchronizer</title>
</head>
<body>

<p class="inline bordered"><b><a href="./CDC_Word_Synchronizer.v">Source</a></b></p>
<p class="inline bordered"><b><a href="./legal.html">License</a></b></p>
<p class="inline bordered"><b><a href="./index.html">Index</a></b></p>

<h1>CDC Word Synchronizer</h1>
<p>Synchronizes the transfer of a word of data from one clock domain to
 another, regardless of relative clock frequencies. Uses ready/valid
 handshakes at the sending and receiving ends, but these can be
 short-circuited for continuous transfers without backpressure: ignore
 <code>sending_ready</code> and tie <code>receiving_ready</code> high. Add
 <code>EXTRA_CDC_DEPTH</code> if you are running near the limits of your silicon
 (consult your vendor datasheets regarding metastability).</p>
<p>The code is laid out in transfer order: we start at the sending handshake,
 convert a signal for new valid data into a level, which passes through CDC
 into the receiving clock domain and completes the receiving handshake. Once
 the receiving handshake completes, we convert that event into a level,
 which passes through CDC back into the sending clock domain to start a new
 sending handshake if there is more data to send.</p>
<p>This module is closely related to the <a href="./CDC_Pulse_Synchronizer_2phase.html">2-phase Pulse
 Synchronizer</a>.</p>
<h2>Operating Notes</h2>
<ul>
<li>When a sending handshake completes, <code>sending_data</code> is latched into
 a register, so you can sample synchronously changing inputs without
 problems.</li>
<li>If a reset happens, you must assert both <code>sending_clear</code> and
 <code>receiving_clear</code> with a (preferably) synchronous reset signal long enough
 to let any level toggle pass through CDC and reach its destination toggle
 register. <em>This takes 3 cycles in both <code>sending_clock</code> and
 <code>receiving_clock</code>.</em></li>
<li>Similarly, if a reset happens, you must wait until both clock domains
 come out of reset before beginning operation, or a CDC transfer may be
 lost.</li>
<li>Set <code>OUTPUT_BUFFER_TYPE</code> to match the desired behaviour at the output of
 the receiving handshake:<ul>
<li>'"HALF"': Uses a <a href="./Pipeline_Half_Buffer.html">Pipeline Half Buffer</a>,
 and will not start the next sending handshake until the receiving handshake
 completes. Use this when sampling a changing signal (e.g.: a counter) or
 to force the processing rate of the sender to match that of the receiver.</li>
<li>'"SKID"': Uses a <a href="./Pipeline_Skid_Buffer.html">Pipeline Skid Buffer</a>,
 which allows the next sending handshake to start before the receiving
 handshake completes. If the transfer from the next sending handshake
 arrives before the first receiving handshake completes, then further
 sending handshakes are blocked until the first receiving handshake
 completes. Use this to allow both ends to send/receive data concurrently.</li>
<li>'"FIFO"': Uses a <a href="./Pipeline_FIFO_Buffer.html">Pipeline FIFO Buffer</a>,
 which allows <code>FIFO_BUFFER_DEPTH</code> sending handshakes to complete before
 blocking. Use this if the downstream pipeline takes in data in bursts.
 The value of <code>FIFO_BUFFER_RAMSTYLE</code> will depend on your device, CAD tool,
 FIFO depth/width, etc...</li>
</ul>
</li>
<li>Set <code>OUTPUT_BUFFER_CIRCULAR</code> to a non-zero value to convert the
   <code>OUTPUT_BUFFER_TYPE</code> to a circular buffer: sending handshakes are always
   accepted, and a full buffer discards the oldest value to make room for the
   newest value. This is useful if you want to let data pass (and be
   discarded) and sample it irregularly, but not cause any backpressure 
   or data duplication.</li>
</ul>
<h2>Latency and Throughput</h2>
<p>The absolute latency from sending to receiving handshake depends on the
 relative sending and receiving clock frequencies, but we can count the
 cycles, in order:</p>
<ul>
<li>1 sending cycle to transform the completion of a sending handshake to a level toggle</li>
<li>1<strong>*</strong> to 3 receiving cycles to do the CDC into the receiving clock domain (and maybe complete a receiving handshake)</li>
<li>1 receiving cycle to transform the completion of the receiving handshake to a level toggle</li>
<li>1<strong>*</strong> to 3 sending cycles to do the CDC into the sending clock domain (and maybe complete a sending handshake)</li>
</ul>
<p><strong>*Corner Case:</strong> The situations where the CDC transfers take 1 cycle in
 either direction are mutually exclusive. The timing of the
 sending/receiving clock edges that makes a CDC crossing in one direction
 take one cycle, is naturally reversed when crossing in the other direction,
 and so cannot happen, and takes the more common 2 to 3 cycles. See the
 "Latency" section in <a href="./cdc.html">A Primer on Clock Domain Crossing (CDC)
 Theory</a> for details. <strong>Unless you know your clocks are
 plesiochronous, it is safer and simpler to ignore this corner case and
 assume 2 to 3 cycles per CDC.</strong> However, we still account for this case 
 in the latency ranges that follow. </p>
<p>Thus, given roughly equal sending and receiving clock rates, a complete
 transfer takes between 5 and 8 sending clock cycles. If the receiving clock
 rate is effectively "infinite", allowing for the whole receiving side to
 finish within a single sending clock cycle, a complete transfer takes 2 to
 4 sending cycles.  If the sending clock rate is similarly effectively
 "infinite" relative to the receiving clock rate, a transfer takes 2 to
 4 receiving clock cycles.</p>
<p>Thus, we can calculate the time for a single transfer as 2 to 4 times the
 sending clock period plus 2 to 4 times the receiving clock period. The
 inverse of that is, of course, the number of transfers per unit time.</p>
<h2>Parameters, Ports, and Constants</h2>

<pre>
`default_nettype none

module <a href="./CDC_Word_Synchronizer.html">CDC_Word_Synchronizer</a>
#(
    parameter WORD_WIDTH                = 0,
    parameter EXTRA_CDC_DEPTH           = 0,
    parameter OUTPUT_BUFFER_TYPE        = "", // "HALF", "SKID", "FIFO"
    parameter OUTPUT_BUFFER_CIRCULAR    = 0,  // non-zero to enable
    parameter FIFO_BUFFER_DEPTH         = 0,  // Only for "FIFO"
    parameter FIFO_BUFFER_RAMSTYLE      = ""  // Only for "FIFO"
)
(
    input   wire                        sending_clock,
    input   wire                        sending_clear,
    input   wire    [WORD_WIDTH-1:0]    sending_data,
    input   wire                        sending_valid,
    output  wire                        sending_ready,

    input   wire                        receiving_clock,
    input   wire                        receiving_clear,
    output  wire    [WORD_WIDTH-1:0]    receiving_data, 
    output  wire                        receiving_valid,
    input   wire                        receiving_ready
);

    localparam WORD_ZERO = {WORD_WIDTH{1'b0}};
</pre>

<h2>From the Sending Handshake</h2>
<p>First, handle the sending handshake. Signal when it completes and we have
 a data word to latch, then wait until we can accept the next word.</p>

<pre>
    wire [WORD_WIDTH-1:0]   sending_handshake_data;
    wire                    sending_handshake_complete;
    wire                    accept_next_word;

    <a href="./Pipeline_to_Pulse.html">Pipeline_to_Pulse</a>
    #(
        .WORD_WIDTH             (WORD_WIDTH)
    )
    sending_handshake
    (
        .clock                  (sending_clock),
        .clear                  (sending_clear),

        // Pipeline input
        .valid_in               (sending_valid),
        .ready_in               (sending_ready),
        .data_in                (sending_data),

        // Pulse interface to connected module input
        .module_data_in         (sending_handshake_data),
        .module_data_in_valid   (sending_handshake_complete),

        // Signal that the module can accept the next input
        .module_ready           (accept_next_word)
    );
</pre>

<p>Then latch the data when the sending handshake completes.</p>

<pre>
    wire [WORD_WIDTH-1:0]   sending_handshake_data_latched;

    <a href="./Register.html">Register</a>
    #(
        .WORD_WIDTH     (WORD_WIDTH),
        .RESET_VALUE    (WORD_ZERO)
    )
    sending_data_storage
    (
        .clock          (sending_clock),
        .clock_enable   (sending_handshake_complete),
        .clear          (sending_clear),
        .data_in        (sending_handshake_data),
        .data_out       (sending_handshake_data_latched)
    );
</pre>

<p>Convert the completion of the sending handshake into a level toggle, which
 initiates a 2-phase asynchronous handshake. This level does not toggle
 again until the completion of the next sending handshake, which since it
 can only happen after the receiving handshake completes, guarantees the
 level stays constant long enough to pass through CDC, regardless of
 relative clock frequency.</p>

<pre>
    wire sending_handshake_toggle;

    <a href="./Register_Toggle.html">Register_Toggle</a>
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    start_async_handshake
    (
        .clock          (sending_clock),
        .clock_enable   (1'b1),
        .clear          (sending_clear),
        .toggle         (sending_handshake_complete),
        .data_in        (sending_handshake_toggle),
        .data_out       (sending_handshake_toggle)
    );
</pre>

<p>Then we synchronize the start of the 2-phase asynchronous handshake into
 the receiving clock domain.</p>

<pre>
    wire sending_handshake_synced;

    <a href="./CDC_Bit_Synchronizer.html">CDC_Bit_Synchronizer</a>
    #(
        .EXTRA_DEPTH        (EXTRA_CDC_DEPTH)  // Must be 0 or greater
    )
    into_receiving
    (
        .receiving_clock    (receiving_clock),
        .bit_in             (sending_handshake_toggle),
        .bit_out            (sending_handshake_synced)
    );
</pre>

<h2>To the Receiving Handshake</h2>
<p>Once in the receiving clock domain, we convert any toggle in level into
 a pulse, which signals new data is available.</p>

<pre>
    wire sending_handshake_data_latched_valid;

    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    convert_async_handshake_sending
    (
        .clock              (receiving_clock),
        .level_in           (sending_handshake_synced),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_posedge_out  (),
        .pulse_negedge_out  (),
        // verilator lint_on  PINCONNECTEMPTY
        .pulse_anyedge_out  (sending_handshake_data_latched_valid)
    );
</pre>

<p>Then we handle the receiving handshake, which is buffered in one of
 multiple ways (see <em>Operating Notes</em>) for different applications.</p>

<pre>
    wire receiving_handshake_complete;

    <a href="./Pulse_to_Pipeline.html">Pulse_to_Pipeline</a>
    #(
        .WORD_WIDTH             (WORD_WIDTH),
        .OUTPUT_BUFFER_TYPE     (OUTPUT_BUFFER_TYPE),       // "HALF", "SKID", "FIFO"
        .OUTPUT_BUFFER_CIRCULAR (OUTPUT_BUFFER_CIRCULAR),   // non-zero to enable
        .FIFO_BUFFER_DEPTH      (FIFO_BUFFER_DEPTH),        // Only for "FIFO"
        .FIFO_BUFFER_RAMSTYLE   (FIFO_BUFFER_RAMSTYLE)      // Only for "FIFO"
    )
    receiving_handshake
    (
        .clock                  (receiving_clock),
        .clear                  (receiving_clear),

        // Pipeline output
        .valid_out              (receiving_valid),
        .ready_out              (receiving_ready),
        .data_out               (receiving_data),

        // Pulse interface from connected module
        .module_data_out        (sending_handshake_data_latched),
        .module_data_out_valid  (sending_handshake_data_latched_valid),

        // Signal that the module can accept the next input
        .module_ready           (receiving_handshake_complete)
    );
</pre>

<p>We then convert the completion of the receiving handshake into a level
 toggle back into the sending clock domain to complete the 2-phase
 handshake. This level does not toggle again until the completion of the
 next receiving handshake, which since it can only happen after the next
 sending handshake completes, guarantees the level stays constant long
 enough to pass through CDC.</p>

<pre>
    wire receiving_handshake_toggle;

    <a href="./Register_Toggle.html">Register_Toggle</a>
    #(
        .WORD_WIDTH     (1),
        .RESET_VALUE    (1'b0)
    )
    finish_async_handshake
    (
        .clock          (receiving_clock),
        .clock_enable   (1'b1),
        .clear          (receiving_clear),
        .toggle         (receiving_handshake_complete),
        .data_in        (receiving_handshake_toggle),
        .data_out       (receiving_handshake_toggle)
    );
</pre>

<p>Then we synchronize the end of the 2-phase handshake into the sending clock
 domain.</p>

<pre>
    wire receiving_handshake_synced;

    <a href="./CDC_Bit_Synchronizer.html">CDC_Bit_Synchronizer</a>
    #(
        .EXTRA_DEPTH        (EXTRA_CDC_DEPTH)  // Must be 0 or greater
    )
    into_sending
    (
        .receiving_clock    (sending_clock),
        .bit_in             (receiving_handshake_toggle),
        .bit_out            (receiving_handshake_synced)
    );
</pre>

<h2>And Back to the Sending Handshake</h2>
<p>Finally, convert the synchronized receiving handshake completion into
 a pulse to start or complete the next sending handshake.</p>

<pre>
    <a href="./Pulse_Generator.html">Pulse_Generator</a>
    convert_async_handshake_receiving
    (
        .clock              (sending_clock),
        .level_in           (receiving_handshake_synced),
        // verilator lint_off PINCONNECTEMPTY
        .pulse_posedge_out  (),
        .pulse_negedge_out  (),
        // verilator lint_on  PINCONNECTEMPTY
        .pulse_anyedge_out  (accept_next_word)
    );

endmodule
</pre>

<hr>
<p><a href="./index.html">Back to FPGA Design Elements</a>
<center><a href="https://fpgacpu.ca/">fpgacpu.ca</a></center>
</body>
</html>

